Skip to content

Don't include more bundles if the requirement is already fulfilled#1618

Closed
laeubi wants to merge 2 commits intoeclipse-pde:masterfrom
laeubi:issue_1604
Closed

Don't include more bundles if the requirement is already fulfilled#1618
laeubi wants to merge 2 commits intoeclipse-pde:masterfrom
laeubi:issue_1604

Conversation

@laeubi
Copy link
Copy Markdown
Contributor

@laeubi laeubi commented Feb 15, 2025

Currently PDE includes everything wired in the target platform even though a requirement might already be fulfilled and only has single cardinality.

This now tracks all capabilities provided and check if a single cardinality is already fulfilled by some of the bundles chosen.

Fix #1604

@HannesWell something for RC1?

@laeubi laeubi requested a review from HannesWell February 15, 2025 10:47
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 15, 2025

Test Results

  273 files   -    12    273 suites   - 12   31m 22s ⏱️ - 14m 41s
  959 tests  - 2 650    903 ✅  - 2 630   52 💤  - 24   4 ❌ + 4 
2 943 runs   - 8 076  2 772 ✅  - 8 016  159 💤  - 72  12 ❌ +12 

For more details on these failures, see this check.

Results for commit 197ac64. ± Comparison against base commit 98a5134.

This pull request removes 2650 tests.
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test1
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test2
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test3
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test4
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test5
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test6
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingAnalysisAntTaskTests ‑ test7
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingApiFreezeAntTaskTests ‑ test1
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingApiFreezeAntTaskTests ‑ test2
org.eclipse.pde.api.tools.anttasks.tests.ApiToolingApiFreezeAntTaskTests ‑ test3
…

♻️ This comment has been updated with latest results.

Copy link
Copy Markdown
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem that a product only contains a sub-set of all bundles in the TP and that therefore the wiring can be different than in the TP is something I thought about a while ago.
While filtering out already provided capabilities as you suggested should have the desired effect (since it's performing a breath-first-search through the complete wiring to compute the closure), I'm not sure if there are cases where just skipping capabilities and thus bundles could break something (plus we have the potential performance problem described below).

When I thought about this general problem, the solution alternative solution I found was to create a new state/wiring resolution for the launch where the bundles explicitly listed in the product (directly or through feature containments) are the 'roots' and are therefore preferred by the resolver. But I havn't checked if it's possible to define such 'roots' or maybe adding them first is already sufficient?

@HannesWell something for RC1?

Better not, as this area has been shown to contain surprises in the past that one doesn't really expect.

Comment on lines +236 to +254
for (BundleCapability bundleCapability : list) {
if (requirement.matches(bundleCapability)) {
return true;
}
}
Copy link
Copy Markdown
Member

@HannesWell HannesWell Feb 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will increase the runtime characteristics of the collection of dependencies by one or two orders (depending on the reference) as for each single requirement the entire namespace is searched again. And namespaces like osgi.wiring.package or osgi.wiring.bundle the namespace is huge, basically all packages or bundles available reside their and it would be checked with a linear search for each single-requirement.

To mitigate that this check should either exclude certain known large namespaces like package and bundle that we are usually not interested anyways (ok, well excluding duplicates could also be interesting for bundles/packages).
Or by introducing a second look-up level that includes the bundle/package name, but I think that's not trivial just on general requirements/capabilities as one has to decompose the filters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that skipping anything is possible here and yes the problem is "hard" but doing a full resolve could even be harder.

We do similar thing on Tycho and even for very large sets of metadata this has never shown any performance problem as this are very simple comparisons.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Feb 15, 2025

I'm not sure if there are cases where just skipping capabilities and thus bundles could break something

I can't think about it right now, but the users always has the chance to influence "the roots" (but can't exclude things what leads to the problem described here).

Basically this is how Tycho/P2 works when building a product, first include the roots and then everything what is required from there (just that it uses deep first if I rember correctly).

But I havn't checked if it's possible to define such 'roots' or maybe adding them first is already sufficient?

This is what actually is the resolver service of OSGi for, but don't expect it to be more efficient, I use such think in the PDE>BND integration in Tycho already so it is possible.

@HannesWell
Copy link
Copy Markdown
Member

Basically this is how Tycho/P2 works when building a product, first include the roots and then everything what is required from there (just that it uses deep first if I rember correctly).

I think that's what we should do in PDE as well to get a clean new state for a launch.
Unfortunately I didn't have the time to look into this before my vacation, but I'll do it when I'm back. Sorry for the delay.

@merks
Copy link
Copy Markdown
Contributor

merks commented Apr 10, 2025

I have major challenges with EMF's development workspace because the current helpful-but-aggressive algorithms drags in RAP dependencies because package imports are resolved/wired to both the real SWT and RAP's RWT. Not only is this not needed, but in fact this makes the self-hosted launch 100% unusable. Working around this is super difficult to specify and maintain (long lists of bundles) as opposed to the current approach of listing a smaller number of features and automatically what's needed is included.

With this PR it works well again for EMF.

As for performance I see it indeed doing a great many linear matches through some very long lists:

isAlreadyProvided for osgi.wiring.bundle size=755
isAlreadyProvided for osgi.ee size=127
isAlreadyProvided for osgi.wiring.package size=10748

The overall time for BundleLauncherHelper.getMergedBundleMapFeatureBased is elapsed=1579 ms.

I don't see a good way to make this perform much better via the API which hide important details about the requirement, e.g., we can't know anything about org.eclipse.osgi.internal.resolver.GenericSpecificationImpl.matchingFilter or the name attribute that will be matched:

image

If we knew the name being matched we could build an index on the name...

Without this logic, I get performance elapsed=11 so it's definitely a brute force algorithm, but the fast result is unusable:

image

@merks
Copy link
Copy Markdown
Contributor

merks commented Apr 10, 2025

I made changes to this version to build an index using information extracted via the APIs:

DependencyManager.java.txt

The performance is then elapsed=236 and elapsed=100 for the second time.

@HannesWell
Copy link
Copy Markdown
Member

HannesWell commented Apr 13, 2025

Thanks Ed for having a look, too.

I also had a look at this, respectively my suggestion to create a new state were the explicit content is preferred, and noticed that the launch validation already creates a new state.So I think that wouldn't take too long, at least I never noticed that. Furthermore I noticed that the DependencyManager is called more often than it seems necessary when launching a product.
I'll try to work out a specific proposal as soon as possible, because I think that would be the cleaner solution. But if that doesn't work out eventually, I think this is the way we have to go.

@merks
Copy link
Copy Markdown
Contributor

merks commented Apr 20, 2025

@HannesWell

M2 is next week so I'm concerned that if this doesn't move forward that we will miss this release cycle due to riskiness. Whatever we change, we should ensure it's tested out in the wild sooner rather than later.

I don't really have a clue of what you think would be a better alternative implementation so I cannot help with that.

@HannesWell
Copy link
Copy Markdown
Member

Thanks for the heads-up.

I don't really have a clue of what you think would be a better alternative implementation so I cannot help with that.

I have just create a PR with my alternative solution: #1725
Unfortunately it does not yet work as expected.

Currently PDE includes everything wired in the target platform even
though a requirement might already be fulfilled and only has single
cardinality.

This now tracks all capabilities provided and check if a single
cardinality is already fulfilled by some of the bundles chosen.
@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented May 3, 2025

I'm now reusing a class from Equinox for the index. I'll prepare a PR with the adjustments as well.

Given that using the resolver will not likely be faster and is currently having issues, I would propose we merge this one for M2 and maybe improve / enhance / replace it with something else later on.

@merks
Copy link
Copy Markdown
Contributor

merks commented May 3, 2025

Testing this with my EMF workspace I have this one problem:

image

Which leads to real problems in the log:

!ENTRY org.eclipse.pde.api.tools.ui 4 0 2025-05-03 07:29:28.485
!MESSAGE FrameworkEvent ERROR
!STACK 0
org.osgi.framework.BundleException: Could not resolve module: org.eclipse.pde.api.tools.ui [315]
  Unresolved requirement: Require-Bundle: org.eclipse.pde.ui; bundle-version="[3.14.200,4.0.0)"
    -> Bundle-SymbolicName: org.eclipse.pde.ui; bundle-version="3.16.100.v20250320-0551"; singleton:="true"
       org.eclipse.pde.ui [336]
         Unresolved requirement: Import-Package: org.bndtools.templating; version="[2.0.0,3.0.0)"
           -> Export-Package: org.bndtools.templating; bundle-symbolic-name="org.bndtools.templating"; bundle-version="7.1.0.202411251545"; version="2.0.0"; uses:="aQute.service.reporter,org.eclipse.core.runtime,org.osgi.framework,org.osgi.service.metatype,org.osgi.util.promise"
              org.bndtools.templating [73]
                Unresolved requirement: Import-Package: com.google.appengine.api; resolution:="optional"
                Unresolved requirement: Import-Package: com.google.apphosting.api; resolution:="optional"
                Unresolved requirement: Import-Package: javax.annotation
         Unresolved requirement: Require-Bundle: org.eclipse.pde.bnd.ui; bundle-version="1.0.0"
           -> Bundle-SymbolicName: org.eclipse.pde.bnd.ui; bundle-version="1.2.100.v20250322-0546"; singleton:="true"
              org.eclipse.pde.bnd.ui [316]
                Unresolved requirement: Import-Package: org.bndtools.templating; version="[2.0.0,3.0.0)"
                  -> Export-Package: org.bndtools.templating; bundle-symbolic-name="org.bndtools.templating"; bundle-version="7.1.0.202411251545"; version="2.0.0"; uses:="aQute.service.reporter,org.eclipse.core.runtime,org.osgi.framework,org.osgi.service.metatype,org.osgi.util.promise"

	at org.eclipse.osgi.container.Module.start(Module.java:495)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel$2.run(ModuleContainer.java:2111)
	at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor$1$1.execute(EquinoxContainerAdaptor.java:146)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:2102)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:2042)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:2004)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1916)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:341)

This problem does not happen with the *.txt version I attached above:

image

So something is not quite greedy enough...

@merks
Copy link
Copy Markdown
Contributor

merks commented May 3, 2025

If I read this correct, it appears that the requirement is ignored because something believes that it's coming from the system bundle:

image

image

@merks
Copy link
Copy Markdown
Contributor

merks commented May 3, 2025

The system bundle exports the union of all EEs:

image

So once the system bundle is added to the capabilities, then many javax things that were removed from the JDK to jakarta will become problematic.

Avoiding to add the system bundle to the capabilities avoids this problem.

	private static void addNewRequiredBundle(BundleDescription bundle, Set<BundleDescription> requiredBundles,
			Queue<BundleDescription> pending, Capabilities capabilities) {
		if (bundle != null && bundle.isResolved() && !bundle.isRemovalPending() && requiredBundles.add(bundle)
				&& !"org.eclipse.osgi".equals(bundle.getSymbolicName())) { //$NON-NLS-1$
			pending.add(bundle);
			capabilities.addCapabilities(bundle);
		}
	}

Yes, that might lead to bundles being included that are not strictly needed because the JDK provides the required package. If we properly populated the state with the actual EE, we'd not need this, but that's a much bigger design change than simply being a bit more pessimistic here.

@HannesWell
Copy link
Copy Markdown
Member

Given that using the resolver will not likely be faster and is currently having issues, I would propose we merge this one for M2 and maybe improve / enhance / replace it with something else later on.

What issues do you mean #1725 has? You have mentioned some points that are not ideal, but I think that has to be accepted and in my testing it worked as desired. But it would be nice if @merks would confirm that as well.

The fundamental problems I see with this approach still exists:

  1. it affects all callers of DependencyManager.findRequirementsClosure(). So even if using the resolve is not faster, it only affects the launching not all other callers (but from my experiences with the validation and TP resolution it is usually fast).
  2. Just ignoring providers could lead to resolution errors at runtime due to class-space violations (as mentioned in Prefer explicitly included plugins for Eclipse/OSGi launches #1725 (comment))
  3. The solution in Prefer explicitly included plugins for Eclipse/OSGi launches #1725 is also less complex.

@merks
Copy link
Copy Markdown
Contributor

merks commented May 3, 2025

I tested #1725 and it works well for my EMF scenario...

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented May 3, 2025

What issues do you mean #1725 has?

You previously wrote

I have just create a PR with my alternative solution: #1725
Unfortunately it does not yet work as expected.

so I assumed it is/was not ready yet.

it affects all callers of DependencyManager.findRequirementsClosure(). So even if using the resolve is not faster, it only affects the launching not all other callers (but from my experiences with the validation and TP resolution it is usually fast).

All callers of findRequirementsClosure currently getting wrong results, so I'm not sure its a bad thing to fix it.

The solution in #1725 is also less complex.

If we ignore the copied code from Equinox, it is 30 simple lines versus 70 lines of highly complex lamda and streaming so I doubt 'less complex' is really true here anyways my main goal is that we get some solution to the problem. It was initially meant to be merged/fixed as soon as master opens, now we have already missed M1 and maybe even will miss M2 as well.

@merks
Copy link
Copy Markdown
Contributor

merks commented May 3, 2025

Let's not miss M2!

@HannesWell
Copy link
Copy Markdown
Member

HannesWell commented May 3, 2025

You previously wrote

I have just create a PR with my alternative solution: #1725
Unfortunately it does not yet work as expected.

so I assumed it is/was not ready yet.

That was referring to the very first state of that PR two weeks ago and was fixed soon afterwards.

it affects all callers of DependencyManager.findRequirementsClosure(). So even if using the resolve is not faster, it only affects the launching not all other callers (but from my experiences with the validation and TP resolution it is usually fast).

All callers of findRequirementsClosure currently getting wrong results, so I'm not sure its a bad thing to fix it.

That statement is not true. They get the correct result according to their current wiring. But as the wiring state is not necessarily unambiguous different solutions are possible. In the general case of the TP-state there is simply not a preference for a specific solution(yet, theoretically we could apply a similar schema when creating the TP-state based on their definition). But in case of a launch the already included bundles are (kind of) preferred, as other providers are (maybe) not necessary and could potentially lead to problems.

my main goal is that we get some solution to the problem. It was initially meant to be merged/fixed as soon as master opens, now we have already missed M1 and maybe even will miss M2 as well.

From my POV #1725 (comment) is ready, maybe you want to give it a try :)

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented May 4, 2025

Then lets close this and merge the other PR asap!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Include required plugins automatically seem not consider already added capabilties

3 participants